import os
import random

import django

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'travel_recommend.settings')
django.setup()
from user.serializers import User, UserModelSerializer, UserComments, UserCommentsSerializer
from travel.serializers import Travel, TravelModelSerializer
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity


# 第一次登录，没有评分过景点的用户，使用未登录推荐
def check_first_login_user(user_id):
    comments = UserComments.objects.filter(user=user_id).values()
    if not comments:
        un_login_analysis(100, 10)


# 未登录推荐
def un_login_analysis(end, recom_len):
    # 获取所有景点
    travels = pd.DataFrame(list(Travel.objects.all().values()))

    # 1. 排序
    # 按评分排序
    score_sort = travels.sort_values(by='score', ascending=False)[:end]
    # 获取景点id
    score_list = score_sort['id'].values.tolist()

    # 按评论数排序
    comment_sort = travels.sort_values(by='comment_total', ascending=False)[:end]
    # 取景点id
    comment_list = comment_sort['id'].values.tolist()

    # 按销售额排序
    sale_count_sort = travels.sort_values(by='sale_count', ascending=False)[:end]
    # 取景点id
    sale_count_list = sale_count_sort['id'].values.tolist()

    # 将所有景点id去重，并存为列表
    travel_id_list = list(set(score_list + comment_list + sale_count_list))

    # 随机取出长度的id
    random_id_list = random.sample(travel_id_list, recom_len)

    # 查询景点数据
    res = []
    for id in random_id_list:
        travel = TravelModelSerializer(Travel.objects.get(id=id)).data
        res.append(travel)

    return res


# 获取用户和景点相似度矩阵
def get_similar_matrix(comments):
    # 1. 去重
    # 用户id数组
    users = pd.unique(comments['user_id'])
    # 景点数组
    travels = pd.unique(comments['travel_id'])
    data_matrix = np.zeros([len(users), len(travels)])

    travel_dic = {travel: i for i, travel in enumerate(travels, 1)}
    travel_dict = {i: travel for i, travel in enumerate(travels, 1)}
    comments['travelId'] = comments['travel_id'].apply(lambda x: travel_dic[x])

    user_dic = {user: i for i, user in enumerate(users, 1)}
    user_dict = {i: user for i, user in enumerate(users, 1)}
    comments['userId'] = comments['user_id'].apply(lambda x: user_dic[x])

    for line in comments.itertuples():
        data_matrix[line.userId - 1, line.travelId - 1] = line.score
    # print(data_matrix)
    return data_matrix, travel_dict, travels, user_dict, user_dic


# 基于用户行为推荐
def similar_user_recom(user_id, comments):
    data_matrix, travel_dict, travels, user_dict, user_dic = get_similar_matrix(comments)
    # 计算用户余弦相似度矩阵
    user_matrix = cosine_similarity(data_matrix)
    # 根据用户查询它的相似用户分数
    similar_users = user_matrix[user_dic[user_id] - 1]

    # 相似度从大到小排序，从1开始切，因为1是自己
    filter_similar_index = np.argsort(similar_users)[::-1][1:]

    # 取出排序好的用户id
    filter_similar_ids = [user_dict[i + 1] for i in filter_similar_index]
    # 取到对应分数
    filter_similar_scores = similar_users[filter_similar_index]

    # 拿到本用户评价的景点
    user_travel_ids = comments.loc[comments['user_id'] == user_id, 'travel_id'].values

    # 每个景点的评分总合、权重之和、景点评论用户数量
    score_sums, weight_sums, count_sums = {}, {}, {}

    # 遍历拿到相似用户索引id和相似度
    for similar_id, similar_score in zip(filter_similar_ids, filter_similar_scores):
        # 拿相似用户评价过的 景点id 和 评分
        similar_travels = comments.loc[comments['user_id'] == similar_id, ['travel_id', 'score']].values
        # 遍历相似用户评价过的 景点id 和 评分
        for similar_travel_id, travel_score in similar_travels:
            similar_travel_id = int(similar_travel_id)
            # 相似用户看过的景点，本用户没看过
            if similar_travel_id not in user_travel_ids:
                # 累加景点评分之和
                if similar_travel_id not in score_sums.keys():
                    score_sums[similar_travel_id] = 0
                score_sums[similar_travel_id] += similar_score * travel_score

                # 计算相似用户评价景点的权重
                if similar_travel_id not in weight_sums.keys():
                    weight_sums[similar_travel_id] = 0
                weight_sums[similar_travel_id] += similar_score

                # 累加相似用户评价数
                if similar_travel_id not in count_sums.keys():
                    count_sums[similar_travel_id] = 0
                count_sums[similar_travel_id] += 1

    # for key, value in score_sums.items():
    #     print(key, value)
    # print('*' * 30)
    # for key, value in weight_sums.items():
    #     print(key, value)
    # print('*' * 30)
    # for key, value in count_sums.items():
    #     print(key, value)
    # print('*' * 30)

    # 初始化推荐
    travel_ranks = {}
    for travel_id, sum_score in score_sums.items():
        if weight_sums[travel_id] > 0:
            # 查询电影
            # travel_ranks[travel_id] = round(sum_score / weight_sums[travel_id], 2)
            travel_ranks[travel_id] = weight_sums[travel_id]

    sort_travel_ranks = sorted(travel_ranks.items(), key=lambda x: x[1], reverse=True)
    # 去除异常值的行
    df = pd.DataFrame(sort_travel_ranks)
    df.dropna(inplace=True)
    return df.values.tolist()


# 已经登录而且有过评价行为的景点推荐
def get_login_recom(user_id):
    # 所有评价数据
    comments = pd.DataFrame(list(UserComments.objects.all().values()))

    user_list = similar_user_recom(user_id, comments)
    # 处理用户相似度推荐结果
    user_result = []
    for u_result in user_list:
        recom_list = list(u_result)
        obj = {
            'travel_id': int(recom_list[0]),
            'recom_score': recom_list[1]
        }
        user_result.append(obj)

    print(user_result)
    return user_result


if __name__ == '__main__':
    # un_login_analysis(100, 10)
    comments = pd.DataFrame(list(UserComments.objects.all().values()))
    # get_similar_matrix(comments)
    # similar_user_recom(1, comments)
    get_login_recom(42)
    """
    33
    [{'travel_id': 77.0, 'recom_score': 5.0}, {'travel_id': 322.0, 'recom_score': 5.0}, {'travel_id': 430.0, 'recom_score': 5.0}, {'travel_id': 2110.0, 'recom_score': 4.5}, {'travel_id': 187.0, 'recom_score': 2.0}, {'travel_id': 23.0, 'recom_score': 1.0}]
    1
    [{'travel_id': 777, 'recom_score': 0.40371786615149335}, {'travel_id': 1699, 'recom_score': 0.40371786615149335}, {'travel_id': 655, 'recom_score': 0.37135279287602746}, {'travel_id': 862, 'recom_score': 0.37135279287602746}]
    42
    [{'travel_id': 1683, 'recom_score': 1.7379167913586717}, {'travel_id': 8, 'recom_score': 1.5326191787643721}, {'travel_id': 676, 'recom_score': 1.4525648714265316}, {'travel_id': 2276, 'recom_score': 1.4112435688285134}, {'travel_id': 2, 'recom_score': 1.0772561530759548}, {'travel_id': 601, 'recom_score': 0.8478714159995736}, {'travel_id': 1970, 'recom_score': 0.8077463877866532}, {'travel_id': 2110, 'recom_score': 0.7427055857520549}, {'travel_id': 1370, 'recom_score': 0.5484039152059591}, {'travel_id': 77, 'recom_score': 0.37135279287602746}, {'travel_id': 322, 'recom_score': 0.37135279287602746}, {'travel_id': 797, 'recom_score': 0.37135279287602746}]
    3
    [{'travel_id': 2276, 'recom_score': 4.57553890865075}, {'travel_id': 2, 'recom_score': 4.360243669407361}, {'travel_id': 2110, 'recom_score': 1.0927391945786613}, {'travel_id': 797, 'recom_score': 0.8638699147898069}, {'travel_id': 23, 'recom_score': 0.6002859838910498}, {'travel_id': 430, 'recom_score': 0.6002859838910498}, {'travel_id': 187, 'recom_score': 0.6002859838910498}, {'travel_id': 77, 'recom_score': 0.5463695972893307}, {'travel_id': 322, 'recom_score': 0.5463695972893307}, {'travel_id': 777, 'recom_score': 0.31750031750047625}, {'travel_id': 1699, 'recom_score': 0.31750031750047625}, {'travel_id': 655, 'recom_score': 0.053916386601719206}, {'travel_id': 862, 'recom_score': 0.053916386601719206}]
    """
